package image

import (
	"encoding/base64"
	"errors"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"os"
)

type IDraw interface {
	Draw(x int, y int) RGBA
}

func (s *RGBA) Draw(x int, y int) RGBA {
	return *s
}

func Clone(i *Image) *Image {
	res := New(i.Width, i.Height, nil)
	for x := 0; x < i.Width; x++ {
		for y := 0; y < i.Height; y++ {
			res.RGBA[x][y].R = i.RGBA[x][y].R
			res.RGBA[x][y].G = i.RGBA[x][y].G
			res.RGBA[x][y].B = i.RGBA[x][y].B
			res.RGBA[x][y].A = i.RGBA[x][y].A
		}
	}
	return res
}

func New(width int, height int, idraw IDraw) (i *Image) {
	i = new(Image)
	i.Width = width
	i.Height = height
	rgba := make([][]RGBA, width, width)
	if idraw == nil {
		idraw = &RGBA{0xFF, 0xFF, 0xFF, 0xFF}
	}
	for x := 0; x < width; x++ {
		rgba[x] = make([]RGBA, height, height)
		for y := 0; y < height; y++ {
			rgba[x][y] = idraw.Draw(x, y)
		}
	}
	i.RGBA = rgba
	return
}

func Read(filePath string) (i *Image, err error) {
	img, e := open(filePath)
	if e != nil {
		return nil, e
	}
	i = new(Image)
	bounds := img.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y
	rawRgba := newRgba(width, height)
	i.Width = width
	i.Height = height
	i.RGBA = rawRgba
	src := convertToNRGBA(img)
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			c := src.At(x, y)
			r, g, b, a := c.RGBA()
			rawRgba[x][y].R = uint8(r)
			rawRgba[x][y].G = uint8(g)
			rawRgba[x][y].B = uint8(b)
			rawRgba[x][y].A = uint8(a)

		}
	}

	return
}

const base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

var coder = base64.NewEncoding(base64Table)

func Base64(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}
	return coder.EncodeToString(data), nil
}

func open(filePath string) (img image.Image, err error) {
	var f *os.File
	f, err = os.Open(filePath)
	if err != nil {
		return
	}
	defer f.Close()
	img, _, err = image.Decode(f)
	return
}

// save a image matrix as a png , if unsuccessful it will return a error
func saveAsPNG(filepath string, i *Image) error {
	width := i.Width
	height := i.Height

	if height == 0 || width == 0 {
		return errors.New("The input of matrix is illegal!")
	}

	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))

	for x := 0; x < i.Width; x++ {
		for y := 0; y < i.Height; y++ {
			nrgba.SetNRGBA(x, y, color.NRGBA{i.RGBA[x][y].R, i.RGBA[x][y].G, i.RGBA[x][y].B, i.RGBA[x][y].A})
		}
	}

	outfile, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer outfile.Close()
	return png.Encode(outfile, nrgba)
}

// save a image matrix as a jpeg,if unsuccessful it will return a error,quality must be 1 to 100
func saveAsJPEG(filepath string, i *Image, quality int) error {
	width := i.Width
	height := i.Height

	if height == 0 || width == 0 {
		return errors.New("The input of matrix is illegal!")
	}

	if quality < 1 {
		quality = 1
	} else if quality > 100 {
		quality = 100
	}

	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))

	for x := 0; x < i.Width; x++ {
		for y := 0; y < i.Height; y++ {
			nrgba.SetNRGBA(x, y, color.NRGBA{i.RGBA[x][y].R, i.RGBA[x][y].G, i.RGBA[x][y].B, i.RGBA[x][y].A})
		}
	}

	outfile, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer outfile.Close()

	return jpeg.Encode(outfile, nrgba, &jpeg.Options{Quality: quality})
}
